/*
 * Copyright (c) 2021 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi/native_api.h"
#include "napi/native_node_api.h"

#include "hilog/log.h"
#include "ffmpeg_utils.h"


using namespace OHOS::HiviewDFX;
using OHOS::HiviewDFX::HiLog;
using OHOS::HiviewDFX::HiLogLabel;


using namespace std;

constexpr HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "MyFfmpegDemo"};
const int SIZE = 1024;

struct VideoCuteAddOnData {
    napi_async_work asyncWork = nullptr;   //异步工作项
    napi_deferred deferred = nullptr;      //用于Promise的resolve,reject
    napi_ref callback = nullptr;           //回调函数
    string args0;                          //第一个参数
    double args1;                          //第二个参数
    double args2;                          //第三个参数
    string args3;                          //第四个参数
    int result = 0;                        //业务逻辑处理结果（返回结果）
};

static void executeVideoCute(napi_env env, void* data) {
    VideoCuteAddOnData *addonData = (VideoCuteAddOnData *) data;
    //调用视频剪切的功能
    addonData->result = FfmpegUtils::videoCute((const char*)addonData->args0.c_str(), \
                                     addonData->args1, \
                                    addonData->args2, \
                                    (const char*)addonData->args3.c_str());
}

static void completeVideoCuteForCallback(napi_env env, napi_status status, void *data) {
    VideoCuteAddOnData *addonData = (VideoCuteAddOnData *) data;
    napi_value callback = nullptr;
    napi_get_reference_value(env, addonData->callback, &callback);
    napi_value undefined = nullptr;
    napi_get_undefined(env, &undefined);
    napi_value result = nullptr;
    napi_create_int32(env, addonData->result, &result);

    //执行回调函数
    napi_value returnVal = nullptr;
    napi_call_function(env, undefined, callback, 1, &result, &returnVal);

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    delete addonData;
}

static void completeVideoCuteForPromise(napi_env env, napi_status status, void *data) {
    VideoCuteAddOnData *addonData = (VideoCuteAddOnData *) data;
    napi_value result = nullptr;
    napi_create_int32(env, addonData->result, &result);
    napi_resolve_deferred(env, addonData->deferred, result);

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    delete addonData;
}

static napi_value videoCute(napi_env env, napi_callback_info info) {
    HiLog::Info(LABEL, "gyf videoCute napi videoCute begin");
    //1. 获取JS传的参数
    size_t argc = 5;
    napi_value args[5] = {nullptr};
    napi_value thisVar = nullptr;
    void* data11;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &(thisVar), &data11));

    

    if (argc == 4) {
        //Promise方式
        //1. 创建Promise
        napi_value promise = nullptr;
        napi_deferred deferred = nullptr;
        napi_create_promise(env, &(deferred), &(promise));


        auto addonData = new VideoCuteAddOnData{
            .asyncWork = nullptr,
            .deferred = deferred,
        };

        //2. 验证参数
        //2.1 验证参数个数
        NAPI_ASSERT(env, argc >= 4, "Wrong number of args");
        
        //2.2 验证参数类型
        napi_valuetype valuetype0;
        NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
        napi_valuetype valuetype1;
        NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
        napi_valuetype valuetype2;
        NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
        napi_valuetype valuetype3;
        NAPI_CALL(env, napi_typeof(env, args[3], &valuetype3));
        
        NAPI_ASSERT(env, \
            valuetype0 == napi_string && \
            valuetype1 == napi_number && \
            valuetype2 == napi_number && \
            valuetype3 == napi_string, \
            "Wrong argument type");
            
        
        //3. NAPI类型转成C/C++类型
        size_t ret = 0;
        char buffer0[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], buffer0, SIZE, &ret));

        addonData->args0 = string(buffer0);
    
        NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args1));
        
        NAPI_CALL(env, napi_get_value_double(env, args[2], &addonData->args2));
        
        char buffer3[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[3], buffer3, SIZE, &ret));
        addonData->args3 = string(buffer3);

        //4. 创建async work，创建成功后通过最后一个参数接受async work的handle
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "videoCute", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, executeVideoCute, completeVideoCuteForPromise, (void *)addonData, &addonData->asyncWork);

        //将创建的async work加到对列中，有底层调度执行
        napi_queue_async_work(env, addonData->asyncWork);
        return promise;
    } else {
        //Callback方式

        auto addonData = new VideoCuteAddOnData{
            .asyncWork = nullptr,
        };

        //2. 验证参数
        //2.1 验证参数个数
        NAPI_ASSERT(env, argc >= 5, "Wrong number of args");
        
        //2.2 验证参数类型
        napi_valuetype valuetype0;
        NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
        napi_valuetype valuetype1;
        NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
        napi_valuetype valuetype2;
        NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
        napi_valuetype valuetype3;
        NAPI_CALL(env, napi_typeof(env, args[3], &valuetype3));
        
        NAPI_ASSERT(env, \
            valuetype0 == napi_string && \
            valuetype1 == napi_number && \
            valuetype2 == napi_number && \
            valuetype3 == napi_string, \
            "Wrong argument type");
        
        //3. NAPI类型转成C/C++类型
        size_t ret = 0;
        char buffer0[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], buffer0, SIZE, &ret));

        addonData->args0 = string(buffer0);
    
        NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args1));
        
        NAPI_CALL(env, napi_get_value_double(env, args[2], &addonData->args2));
        
        char buffer3[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[3], buffer3, SIZE, &ret));
        addonData->args3 = string(buffer3);

        NAPI_CALL(env, napi_create_reference(env, args[4], 1, &addonData->callback));

        //4. 创建async work
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "videoCute", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, executeVideoCute, completeVideoCuteForCallback, (void *)addonData, &addonData->asyncWork);

        //将创建的async work加到队列中，由底层调度执行
        napi_queue_async_work(env, addonData->asyncWork);

        napi_value result = 0;
        NAPI_CALL(env, napi_get_null(env, &result));

        return result;
    }   
}


struct VTAHAddOnData {
    napi_async_work asyncWork = nullptr;   //异步工作项
    napi_deferred deferred = nullptr;      //用于Promise的resolve,reject
    napi_ref callback = nullptr;           //回调函数
    string args0;                          //3个输入参数
    string args1;
    string args2;
    int result = 0;                        //业务逻辑处理结果（返回结果）
};

static void executeVTAH(napi_env env, void* data) {
    VTAHAddOnData *addonData = (VTAHAddOnData *) data;
    //调用视频分离函数
    addonData->result = FfmpegUtils::videoToAacH264(
        (const char*)addonData->args0.c_str(), \
        (const char*)addonData->args1.c_str(), \
        (const char*)addonData->args2.c_str() \
        );
    
}

static void completeVTAHForCallback(napi_env env, napi_status status, void *data) {
    VTAHAddOnData *addonData = (VTAHAddOnData *) data;
    napi_value callback = nullptr;
    napi_get_reference_value(env, addonData->callback, &callback);
    napi_value undefined = nullptr;
    napi_get_undefined(env, &undefined);
    napi_value result = nullptr;
    napi_create_int32(env, addonData->result, &result);

    //调用callback
    napi_value returnVal = nullptr;
    napi_call_function(env, undefined, callback, 1, &result, &returnVal);

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    delete addonData;
}

static void completeVTAHForPromise(napi_env env, napi_status status, void *data) {
    VTAHAddOnData *addonData = (VTAHAddOnData *) data;
    napi_value result = nullptr;
    napi_create_int32(env, addonData->result, &result);
    napi_resolve_deferred(env, addonData->deferred, result);

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    delete addonData;
}

static napi_value videoToAacH264(napi_env env, napi_callback_info info) {
    HiLog::Info(LABEL, "gyf videoCute napi videoToAacH264 begin");
    //1. 获取JS传的参数
    size_t argc = 4;
    napi_value args[4] = {nullptr};
    napi_value thisVar = nullptr;
    void* data11;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &(thisVar), &data11));

    if (argc == 3) {
        //Promise方式
        //1. 创建Promise
        napi_value promise = nullptr;
        napi_deferred deferred = nullptr;
        napi_create_promise(env, &(deferred), &(promise));


        auto addonData = new VTAHAddOnData {
            .asyncWork = nullptr,
            .deferred = deferred,
        };

        //2. 验证参数
        //2.1 验证参数个数
        NAPI_ASSERT(env, argc >= 3, "Wrong number of args");
        
        //2.2 验证参数类型
        napi_valuetype valuetype0;
        NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
        napi_valuetype valuetype1;
        NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
        napi_valuetype valuetype2;
        NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
        
        NAPI_ASSERT(env, \
            valuetype0 == napi_string && \
            valuetype1 == napi_string && \
            valuetype2 == napi_string , \
            "guoyuefeng Wrong argument type");
            
        //3. NAPI类型转成C/C++类型
        size_t ret = 0;
        char buffer0[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], buffer0, SIZE, &ret));
        addonData->args0 = string(buffer0);
    
        char buffer1[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], buffer1, SIZE, &ret));
        addonData->args1 = string(buffer1);
        
        char buffer2[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[2], buffer2, SIZE, &ret));
        addonData->args2 = string(buffer2);

        //4. 创建async work
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "videoToAacH264", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, executeVTAH, completeVTAHForPromise, (void *)addonData, &addonData->asyncWork);

        //将创建的async work加入到队列中，由底层调度执行
        napi_queue_async_work(env, addonData->asyncWork);
        return promise;
    } else {
        //Callback方式
        auto addonData = new VTAHAddOnData {
            .asyncWork = nullptr,
        };

        //2. 验证参数
        //2.1 验证参数个数
        NAPI_ASSERT(env, argc >= 4, "Wrong number of args");
        
        //2.2 验证参数类型
        napi_valuetype valuetype0;
        NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
        napi_valuetype valuetype1;
        NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
        napi_valuetype valuetype2;
        NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
        
        NAPI_ASSERT(env, \
            valuetype0 == napi_string && \
            valuetype1 == napi_string && \
            valuetype2 == napi_string , \
            "Wrong argument type");
        
        //3. NAPI类型转成C/C++类型
        size_t ret = 0;
        char buffer0[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], buffer0, SIZE, &ret));
        addonData->args0 = string(buffer0);
    
        char buffer1[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[1], buffer1, SIZE, &ret));
        addonData->args1 = string(buffer1);
        
        char buffer2[SIZE];
        NAPI_CALL(env, napi_get_value_string_utf8(env, args[2], buffer2, SIZE, &ret));
        addonData->args2 = string(buffer2);

        NAPI_CALL(env, napi_create_reference(env, args[3], 1, &addonData->callback));

        //4. 创建async work，创建成功后通过最后一个参数接受async work的handle
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "videoToAacH264", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, executeVTAH, completeVTAHForCallback, (void *)addonData, &addonData->asyncWork);

        //5. 将创建的async work加入到队列中，由底层调度执行
        napi_queue_async_work(env, addonData->asyncWork);

        napi_value result = 0;
        NAPI_CALL(env, napi_get_null(env, &result));

        return result;
    }   
}


/***********************************************
 * Module export and register
 ***********************************************/
static napi_value registerMyffmpegdemo(napi_env env, napi_value exports)
{

    static napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("videoCute", videoCute),
        DECLARE_NAPI_FUNCTION("videoToAacH264", videoToAacH264),
    };
    

    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}

//模块定义
static napi_module myffmpegdemoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerMyffmpegdemo,
    .nm_modname = "myffmpegdemo",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

//注册模块
extern "C" __attribute__((constructor)) void MyffmpegdemoRedister(void) {
    napi_module_register(&myffmpegdemoModule);
}
